#pragma once

#include <zenox/glm.hpp>
#include <array>

namespace zenoui {

struct Rect {
    glm::vec2 m_topleft{};
    glm::vec2 m_size{};

    static Rect fromTopleftSize(glm::vec2 const &topleft, glm::vec2 const &size) {
        return {topleft, size};
    }

    static Rect fromCenterSize(glm::vec2 const &center, glm::vec2 const &size) {
        return {center - size * 0.5f, size};
    }

    bool contains(glm::vec2 const &pos) const {
        return glm::all(glm::greaterThanEqual(pos, m_topleft)) && glm::all(glm::lessThan(pos - m_topleft, m_size));
    }

    bool empty() const {
        return m_size.x <= 0 || m_size.y <= 0;
    }

    Rect intersectWith(Rect const &that) const {
        auto topleft = glm::max(that.m_topleft, m_topleft);
        auto bottomright = glm::min(that.m_topleft + that.m_size, m_topleft + m_size);
        Rect ret{topleft, bottomright - topleft};
        if (ret.m_size.x <= 0 || ret.m_size.y <= 0) {
            return Rect{};
        }
        return ret;
    }

    Rect unionWith(Rect const &that) const {
        auto topleft = glm::min(that.m_topleft, m_topleft);
        auto bottomright = glm::max(that.m_topleft + that.m_size, m_topleft + m_size);
        return Rect{topleft, bottomright - topleft};
    }

    glm::vec2 size() const {
        return m_size;
    }

    float width() const {
        return m_size.x;
    }

    float height() const {
        return m_size.y;
    }

    float left() const {
        return m_topleft.x;
    }

    float top() const {
        return m_topleft.y;
    }

    float right() const {
        return m_topleft.x + m_size.x;
    }

    float bottom() const {
        return m_topleft.y + m_size.y;
    }

    glm::vec2 topleft() const {
        return m_topleft;
    }

    glm::vec2 topright() const {
        return m_topleft + glm::vec2(m_size.x, 0);
    }

    glm::vec2 bottomleft() const {
        return m_topleft + glm::vec2(0, m_size.y);
    }

    glm::vec2 bottomright() const {
        return m_topleft + m_size;
    }

    glm::vec2 center() const {
        return m_topleft + m_size * 0.5f;
    }

    void extendMargin(glm::vec2 bound) {
        /* bound = glm::max(-m_size * 0.5f, glm::min(m_size * 0.5f, bound)); */
        m_topleft -= bound;
        m_size = glm::max(glm::vec2(0), m_size + bound * 2.0f);
    }

    void extendMargin(float bound) {
        extendMargin(glm::vec2(bound));
    }

    void extendMargin(glm::vec2 const &lt, glm::vec2 const &rb) {
        m_size = glm::max(glm::vec2(0), m_size + lt + rb);
        m_topleft -= lt;
    }

    void extendMargin(float l, float r, float t, float b, float dir = 1.0f) {
        auto lt = glm::vec2(l, t) * dir;
        auto rb = glm::vec2(r, b) * dir;
        extendMargin(lt, rb);
    }
};

}
